/*
 * Copyright (C) 2011 ARNDroid
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package br.com.arndroid.lib.entity;

import br.com.arndroid.lib.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.ListView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import greendroid.app.GDListActivity;
import greendroid.widget.ActionBarItem;
import greendroid.widget.ActionBarItem.Type;

/**
 * A interface for entity lists and access to CRUD operations in that entities.
 *
 * <p>
 * This class apresent the CRUD and the list for your entity with GreendDroid interface.
 * </p>
 *
 * <p>
 * There are <a href="#Preconditions">preconditions</a>.
 * </p>
 *
 * <a name="Preconditions"></a>
 * <h4>Preconditions</h4>
 * <p>
 * You must have:
 * <ol>
 * 	<li>For entity manipulation:
 * 	<ul>
 * 		<li>a related {@link br.com.arndroid.lib.entity.EntityPOJO EntityPOJO} subclass;</li>
 * 		<li>a related {@link br.com.arndroid.lib.entity.EntityAdapter EntityAdapter} subclass;</li>
 * 		<li>a related {@link br.com.arndroid.lib.entity.EntityController EntityController} implementation;</li>
 *  	<li>a related {@link br.com.arndroid.lib.entity.EntityFormActivity EntityFormActivity} subclass.</li>
 *  </ul>
 *  </li>
 *  <li>For interface manipulation:
 *  <ul>
 *  	<li>the empty list layout xml;</li>
 *  	<li>the option menu xml;</li>
 *  	<li>the context menu xml.</li>
 *  </ul>
 *  </li>
 *  <li>For i18n manipulation:
 *  <ul>
 *  	<li>the lower case entity name;</li>
 *  	<li>the the title for delete dialog;</li>
 *  	<li>the message for delete in the dialog.</li>
 *  </ul>
 *  </li>
 * </ol> 
 * </p>
 *
 * @author Alceu
 * @param <T>	the entity to be managed
 */
public abstract class EntityListActivity<T extends EntityPOJO> extends GDListActivity {
	
	// Dialogs constants:
	private static final int DIALOG_ID_DELETE = 1;
	
	private static final int REQ_CODE_EDIT = 2;

	// Request codes constants:
	private static final int REQ_CODE_NEW = 1;

	// Controller:
	private EntityController<T> mController;
	
	// Class of EntityManager:
	private Class<? extends EntityFormActivity> mEntityFormClass;
	
	// Id for delete operation:
	private long mIdForDelete;
	
	/**
	 * Constructor.
	 * 
	 * @param entityFormClass the form for this entity
	 */
	protected EntityListActivity(Class<? extends EntityFormActivity> entityFormClass) {
		mEntityFormClass = entityFormClass;
	}
	
	/**
	 * Return a new Adapter configured with the informed cursor.
	 * <p>
	 * This method will be called in each successful CRUD operation with a new cursor
	 * representing the updated data set. 
	 * 
	 * @param cur the cursor
	 * @return the new adapter configured with the cursor
	 */
	public abstract EntityAdapter getNewAdapter(Cursor cur);
	
	/**
	 * Return the id for the context menu wich <i>must</i>
	 * contain a reference to {@code android:id="@id/arnEntityEdit"} on the edit menu
	 * <i>and</i> a reference to {@code android:id="@id/arnEntityDelete"} on the delete menu. 
	 * 
	 * @return the id for the context menu
	 */
	public abstract int onAcquireContextMenuId();
	
	/**
	 * Return the controller of the entity.
	 * 
	 * @return the controller of the entity
	 */
	public abstract EntityController<T> onAcquireController();
	
	/**
	 * Return the delete dialog message.
	 * 
	 * @return the delete dialog message
	 */
	public abstract String onAcquireDeleteDialogMessage();
	
	/**
	 * Return the delete dialog title.
	 * 
	 * @return the delete dialog title
	 */
	public abstract String onAcquireDeleteDialogTitle();
	
	/**
	 * Return the the layout id for a empty list
	 * wich <i>must</i> contain a reference to {@code android:id="@android:id/empty"}
	 * 
	 * @return the layout id for a empty list
	 */
	public abstract int onAcquireEmptyLayoutId();

	// MEMBER FIELDS
	
	/**
	 * Must override this to return the name in lower case
	 * of the entity (used in dialogs).
	 * 
	 * @return the name in lower case of the entity
	 */
	public abstract String onAcquireLCaseEntityName();
	
	/**
	 * Return the id for the option menu
	 * wich <i>must</i> contain a reference to {@code android:id="@id/arnEntityAdd"}
	 * on the add menu.
	 * 
	 * @return the id for the option menu
	 */
	public abstract int onAcquireOptionMenuId();
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		
		switch(requestCode) {
		case REQ_CODE_NEW:
			if(resultCode == RESULT_OK) 
				onFillData();
			break;
		case REQ_CODE_EDIT:
			if(resultCode == RESULT_OK)  {
				if(data.getExtras().getBoolean(EntityFormActivity.EXTRA_ENTITY_PERSISTABLE))
					onFillData();
			}
			break;
		default:
			throw new IllegalArgumentException("Invalid requestCode inside EntityListActivity.onActivityResult(requestCode, resultCode, data)");
		}
	}

	// LIFE-CICLE
	
	// Add a new entity. Override if you need special behavior:
	protected void onAdd() {
		Intent i = new Intent(this, mEntityFormClass);
		i.setAction(EntityFormActivity.ENTITY_ACTION_NEW);
		startActivityForResult(i, REQ_CODE_NEW);
	}
	
	protected void onConfigureActionBar() {
		addActionBarItem(Type.Add, R.id.arnEntityAdd);
	}

	// SCREEN GADGETS
	
	@Override
	public boolean onContextItemSelected(MenuItem item) {
		AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); 
		if (item.getItemId() == R.id.arnEntityEdit) {
			onEdit(info.id);
			return true;
		} else if (item.getItemId() == R.id.arnEntityDelete) {
			onDelete(info.id);
			return true;
		} else {
			throw new IllegalArgumentException("Invalid item.getItemId() inside EntityListActivity.onContextItemSelected(item)");
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		final ListView listView = getListView();
		
		super.onCreate(savedInstanceState);
		
		// Set the controller:
		mController = onAcquireController();
		
		// Set the empty list view:
		getLayoutInflater().inflate(onAcquireEmptyLayoutId(), (ViewGroup) listView.getParent());
		listView.setEmptyView(findViewById(android.R.id.empty));
		
		// Register the list for context menu:
		registerForContextMenu(listView);
		
		// Configure the action bar:
		onConfigureActionBar();
		
		// Initial data load:
		onFillData();
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
		super.onCreateContextMenu(menu, v, menuInfo);
		
		getMenuInflater().inflate(onAcquireContextMenuId(), menu);
		menu.setHeaderTitle(mController.getById(((AdapterContextMenuInfo) menuInfo).id).getName());
	}
	
	// MENUS
	
	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_ID_DELETE:
			return new AlertDialog.Builder(this)
			.setTitle(onAcquireDeleteDialogTitle())
			// Hack - If not set, the message customization in onPrepareDialog(id, dialog) fails:
			.setMessage("")
			.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int dialog_id) {
					mController.remove(mIdForDelete);
					dialog.dismiss();
					onFillData();
				}
			})
			.setNegativeButton(android.R.string.no, null)
			.create();
		default:
			throw new IllegalArgumentException("Invalid id inside EntityListActivity.onCreateDialog(id)");
		}
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
	    getMenuInflater().inflate(onAcquireOptionMenuId(), menu);
	    return true;
	}
	
	// Delete the entity. Override if you need special behavior:
	protected void onDelete(final long id) {
		mIdForDelete = id;
		showDialog(DIALOG_ID_DELETE);
		// Delete operation occurs in dialog onClick listner, if user confirmed.
	}
	
	// Edit the entity. Override if you need special behavior:
	protected void onEdit(final long siteId) {
		Intent i = new Intent(this, mEntityFormClass);
		i.setAction(EntityFormActivity.ENTITY_ACTION_UPDATE);
		i.putExtra(EntityFormActivity.EXTRA_ENTITY_ID, siteId);
		startActivityForResult(i, REQ_CODE_EDIT);
	}

	// DIALOG
	
	// Fill the list with data. Override if you need special behavior:
	protected void onFillData() {
		final Cursor cursor = mController.getAll();
		startManagingCursor(cursor);
		setListAdapter(getNewAdapter(cursor));
	}
	
	@Override
	public boolean onHandleActionBarItemClick(ActionBarItem item, int position) {
		if (item.getItemId() == R.id.arnEntityAdd) {
			onAdd();
		} else {
			throw new IllegalArgumentException("Invalid item.getItemId() inside EntityListActivity.onHandleActionBarItemClick(item, position)");
		}
		return true;
	}
	
	// INTERNAL
	
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		onEdit(id);
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    if (item.getItemId() == R.id.arnEntityAdd) {
			onAdd();
			return true;
		} else {
			throw new IllegalArgumentException("Invalid item.getItemId() inside EntityListActivity.onOptionsItemSelected(item)");
		}
	}
	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		switch (id) {
		case DIALOG_ID_DELETE:
			((AlertDialog)dialog).setMessage(String.format(onAcquireDeleteDialogMessage(), onAcquireLCaseEntityName(), mController.getById(mIdForDelete).getName()));
			break;
		default:
			throw new IllegalArgumentException("Invalid id inside EntityListActivity.onPrepareDialog(id, dialog)");
		}
	}
}
